# -*- encoding: utf-8 -*-

"""
------------------------------------------
@File       : life_game_v2.py
@Author     : maixiaochai
@Email      : maixiaochai@outlook.com
@CreatedOn  : 2021/7/20 15:49
------------------------------------------
    生命游戏第二版：粗糙的多线程方案
    缺点：
        1）要专门采用工具来协调这些Thread实例，才能保证它们不会破坏程序中的数据，较为复杂，随着时间的推移更加难以扩展与维护
        2）线程占用的内存比较多，每条线程大约占8MB，如果单元格数量较多，给每项操作都开一个线程是不现实的
        3）线程的开销比较大，系统频繁地切换线程，会降低程序的运行效率
        4）如果线程所执行的代码出现了错误，也会很难调试，比如，game_logic函数有可能会抛出异常，
            因为它要执行的那种 I/O操作本来就属于有时成功有时失败的操作,
            并且线程本身并不会把执行过程中遇到的异常抛给启动线程或者等待改线程完工的那个“人”，这种情况很难调试
"""
from threading import Lock, Thread
from time import sleep

from life_game_v1 import Grid, step_cell, demo

ALIVE = '*'
EMPTY = '-'


class LockingGrid(Grid):
    def __init__(self, height, width):
        super().__init__(height, width)
        self.lock = Lock()

    def __str__(self):
        with self.lock:
            return super().__str__()

    def get(self, y, x):
        with self.lock:
            return super().get(y, x)

    def set(self, y, x, state):
        with self.lock:
            return super().set(y, x, state)


def game_logic(state, neighbors):
    """ 游戏逻辑"""
    if state == ALIVE:
        if neighbors < 2:
            return EMPTY  # Die: too few

        elif neighbors > 3:  # Die: too many
            return EMPTY

    else:
        if neighbors == 3:
            return ALIVE  # Regenerate

    # 模拟网络访问耗时，阻塞式
    # 可以想象成每个相邻的格子都代表一个网络游戏玩家
    sleep(0.1)

    return state


def simulate_threaded(grid):
    """
        仿真，多线程版
    """
    next_grid = Grid(grid.height, grid.width)

    threads = []
    for y in range(grid.height):
        for x in range(grid.width):
            args = (y, x, grid.get, next_grid.set)
            thread = Thread(target=step_cell, args=args)
            thread.start()  # fan out
            threads.append(thread)

    for thread in threads:
        thread.join()  # fan in

    return next_grid


if __name__ == '__main__':
    demo(LockingGrid)
